home *** CD-ROM | disk | FTP | other *** search
/ Fritz: All Fritz / All Fritz.zip / All Fritz / FILES / UTILFILE / NJRAMD.LZH / NJRAMD.ASM < prev    next >
Assembly Source File  |  1991-06-23  |  42KB  |  1,436 lines

  1.  
  2. ;  Nifty James' Famous Expanded Memory Disk Drive
  3. ;  (C) Copyright 1987 by Mike Blaszczak.  All Rights Reserved
  4.  
  5. ;  Version 1.01  of  24 May 1987
  6. ;  Version 1.10  of  25 May 1987
  7. ;  Version 1.15  of  31 May 1987
  8. ;  Version 1.20  of  16 Oct 1987
  9. ;  Version 1.30  of  05 Dec 1989
  10. ;  Version 1.40  of  23 Jun 1991 
  11.  
  12. ;  Shareware  $15     Please register!
  13.  
  14. ;  Assemble with
  15. ;    MASM NJRAMD;
  16. ;        (Use MASM /DV286 NJRAMD; to assemble 286 version.)
  17. ;    LINK NJRAMD;
  18. ;    EXE2BIN NJRAMD.EXE NJRAMD.SYS
  19. ;    DEL NJRAMD.EXE
  20.  
  21. ;  --> DEVICE DRIVER FORMAT FILE <--
  22. ;  -->  REMEMBER TO USE EXE2BIN  <--
  23.  
  24. ; ---------------------------------------------------------------------------
  25.  
  26. ;   ASCII Characters
  27.  
  28. bell        equ    7        ; bell character
  29. tab        equ    9        ; tab character
  30. lf        equ    10        ; linefeed
  31. cr        equ    13        ; carriage return
  32. space        equ    32        ; space
  33. eos        equ    '$'        ; end of DOS string
  34.  
  35. ; ---------------------------------------------------------------------------
  36.  
  37. EMM        equ    067h        ; the E/EMS memory manager
  38.  
  39. ; ---------------------------------------------------------------------------
  40. ;   I/O Ports
  41.  
  42. Speak        equ    061h        ; speaker port
  43. SpeakMask    equ    011111110b    ; mask for speaker set bit
  44. SpeakToggle    equ    000000010b    ; toggle bit for the speaker
  45.  
  46. ; ---------------------------------------------------------------------------
  47. ;   DOS Calls
  48.  
  49. ; These are DOS functions used by the driver.
  50.  
  51. DisplayOut    equ    002h        ; call to print a single character
  52. PrintString    equ    009h        ; call to print a '$' string
  53. GetDOSVersion    equ    030h        ; call to get the DOS version #
  54.  
  55. ; ---------------------------------------------------------------------------
  56. ;  E/EMM Routines
  57.  
  58. ; These are the E/EMM functions that we use.  (These are specific functions
  59. ; of the EMM interrupt.)
  60.  
  61. E_PageBase    equ    041h        ; determine the Page Fram Base Addr
  62. E_Counts    equ    042h        ; determine free/total mem
  63. E_Open        equ    043h        ; open, allocate, obtain handle ID
  64. E_MapPage    equ    044h        ; map a logical page into window
  65. E_Version    equ    046h        ; get the E/EMM version number
  66. E_Save        equ    047h        ; save mapping context
  67. E_Restore    equ    048h        ; restore mapping context
  68.  
  69. ; ---------------------------------------------------------------------------
  70. ;  Driver Equates
  71.  
  72. ; This is the media descriptor byte.  Since our RAM drive is not 2 sided,
  73. ; does not have 8 sectors per track, and is not removable, we use 0F8h.
  74. ; At least, that's what the IBM DTR manual says.
  75.  
  76. MediaD        equ    0F8h
  77.  
  78. ; These are equates used by the driver.  They are all status and
  79. ; error flags, as defined in the DOS Technical Reference Manual.
  80.  
  81. ;                        FEDCBA9876543210 <- BIT NUMBERS
  82. errorflag    equ    01000000000000000b    ; error bit flag
  83. busystat    equ    00000001000000000b    ; busy status bit flag
  84. donestat    equ    00000000100000000b    ; done status bit flag
  85.  
  86. err_writeprot    equ    0        ; write protect violation
  87. err_badunit    equ    1        ; unknown unit number
  88. err_notready    equ    2        ; device not ready
  89. err_unknown    equ    3        ; unknown command
  90. err_CRC        equ    4        ; error CRC command
  91. err_reqlen    equ    5        ; bad request length
  92. err_seek    equ    6        ; seek failure
  93. err_badmedia    equ    7        ; bad media
  94. err_badsector    equ    8        ; sector not found
  95. err_badwrite    equ    10        ; write fault
  96. err_badread    equ    11        ; read fault
  97. err_general    equ    12        ; general failure
  98.  
  99. ; ---------------------------------------------------------------------------
  100. ;  Structure Definitions
  101.  
  102. ;  The structures defined here are used to find information in the
  103. ;  various request header formats.  Of course, being structures, they
  104. ;  don't take up space... they are used to define offsets for the
  105. ;  addressing of the request header.
  106.  
  107. rq    equ    es:[bx]            ; base address used in routines
  108.  
  109. ;  -- Request Header (General Format)
  110.  
  111. rhead    struc
  112.     rlen    db    ?    ; length of the structure
  113.     unitn    db    ?    ; unit number
  114.     command    db    ?    ; command code
  115.     status    dw    ?    ; status code (returned by us)
  116.         db    8 dup(?); reserved bytes
  117. rhead    ends
  118.  
  119.  
  120. ;  -- Request Header (INIT Command)
  121.  
  122. inithead    struc
  123.         db    (type rhead) dup (?)
  124.     units    db    ?    ; number of units
  125.     ndadro    dw    ?    ; ending address offset
  126.     ndadrs    dw    ?    ; ending address segment
  127.     bpboff    dw    ?    ; BPB offset pointer
  128.     bpbseg    dw    ?    ; BPB segment pointer
  129.     taglet    db    ?    ; drive tag letter
  130. inithead    ends
  131.  
  132. ;  -- Request Header (Media Check)
  133.  
  134. mediahead    struc
  135.         db    (type rhead) dup (?)
  136.     media    db    ?    ; our meida descriptor byte
  137.     change    db    ?    ; changed media flag
  138. mediahead    ends
  139.  
  140. ;  -- Request Header (Build BPB)
  141.  
  142. bbpbhead    struc
  143.         db    (type rhead) dup (?)
  144.         db    ?    ; media descriptor byte
  145.     baoff    dw    ?    ; transferr buffer address offset
  146.     baseg    dw    ?    ; transferr buffer address segment
  147.         dw    ?    ; BIOS parameter block pointer
  148.         dw    ?    ; BIOS parameter block pointer
  149. bbpbhead    ends
  150.  
  151. ;  -- Request Header (Read and Write)
  152.  
  153. rwhead        struc
  154.         db    (type rhead) dup (?)
  155.         db    ?    ; media descriptor byte
  156.     tbaoff    dw    ?    ; transferr buffer address offset
  157.     tbaseg    dw    ?    ; transferr buffer address segment
  158.     count    dw    ?    ; sector count
  159.     strtsec    dw    ?    ; starting sector number
  160. rwhead        ends
  161.  
  162.  
  163. ; With these headers defined as they are, access to the request header
  164. ; and command info fields is greatly simplified.  By setting ES:BX to
  165. ; point to the request header, the information can be easily referenced
  166. ; by using constructs such as
  167.  
  168. ;        mov    rq.count,ax
  169. ;  or
  170. ;        mov    al,rq.command
  171.  
  172. ; Note that any part of the program can easily reference any particular
  173. ; command's structure, since the line
  174.  
  175. ;        db    (type rhead) dup (?)
  176.  
  177. ; makes all the command-specific structures "equivalent".
  178.  
  179. ; Check to see if this is the 286 version
  180.  
  181. ifdef  V286
  182.     .286
  183.     if1
  184.         %OUT    Enhanced processor version
  185.     endif
  186.     ifdef    PCL
  187.     if1
  188.         %OUT    for the PC's Limited 286/386
  189.     endif
  190.     endif
  191. else
  192.     if1
  193.         %OUT    Standard Version
  194.     endif
  195. endif
  196.  
  197.  
  198. ;  This macro is used during debugging.  It prints a single character
  199. ; via the BIOS screen interface, and leaves the registers unchanged.
  200.  
  201. ifdef    DEBUG
  202.  
  203.     if1
  204.         %OUT  DEBUG Version
  205.     endif
  206.     PrintChar    macro    Char
  207.         ifdef    PCL
  208.  
  209.             push    ax
  210.             mov    al,Char
  211.             out    095h,al        ; put it digit 3 of smartvu
  212.             pop    ax
  213.  
  214.         else
  215.     
  216.             push    ax        ; save the regs
  217.             push    bx
  218.             push    dx
  219.             mov    ah,15
  220.             int    010h        ; get the current page
  221.             mov    al,Char
  222.             mov    ah,14        ; print the character
  223.             int    010h
  224.  
  225.             xor    dx,dx
  226.             mov    ah,0        ; also to printer
  227.             mov    al,Char
  228.             int    017h
  229.  
  230.             pop    dx
  231.             pop    bx        ;restore the regs
  232.             pop    ax
  233.  
  234.             endif
  235.             endm
  236.  
  237. else
  238.     PrintChar    macro    Char        ; if not debugging, blow it off
  239.             endm
  240. endif
  241.  
  242. ; ---------------------------------------------------------------------------
  243. ;  Public declarations for SYMDEB
  244.  
  245. ; These are public declarations included to allow SYMDEB to know where
  246. ; various lables and addresses are.  They are only needed for debugging,
  247. ; and serve no other useful purpose.
  248.  
  249. PUBLIC NextPlace
  250. PUBLIC Attrib, JumpTable, TopCommand, RBPoint, RBPointOff, RBPointSeg, SaveSS
  251. PUBLIC SaveSP, SaveAX, EMMHandle, EMMBase, StackTop, STRATPROC, Strategy
  252. PUBLIC INTPROC, Interrupt, NoSaveM, FreakOut, IOCTLInput, ReadNoWait
  253. PUBLIC InputStatus, InputFlush, badcommand, BigLog, NoRestore, MC, MediaCheck
  254. PUBLIC BBPB, BuildBPB, BPBArray, OurBoot, OurBPB, SecSize, SecPerCluster
  255. PUBLIC RDirLen, DiskSize, SecPerFAT, BootCode, TAddr, TAddrOff, TAddrSeg
  256. PUBLIC TDone, TCount, TSector, RSEC, Read, ReadLoop, ReadDone
  257. PUBLIC ReadFinish, ReadError, WSEC, Write, WriteLoop, WriteDone, WriteFinish
  258. PUBLIC WriteError, CLIPPER, RangeError, InRange, SPEAKERCLICK, MakeClick
  259. PUBLIC NoClick, SpeakerFlag, LastTime, GS, GetSector, CantGet, LastResident
  260. PUBLIC EMMPresent, GenFailHook, EMMPresent2, MemForMe, EatingWhite, GotOption
  261. PUBLIC NoBump, NotSilence, PagesLoop, LastDigit, NotPages, NotUseAll
  262. PUBLIC Unrecognized, EndOfLine, GoodSize, GotPages, BigBust, ReTry
  263. PUBLIC GoodCombo, NoKludge, WipeOut, WipeOut2, FindFree, CalcEMMFree
  264. PUBLIC CalcDiskFree, ClickOkay, MsgOkay, InitFail, GenFail, HowMuch
  265. PUBLIC RqdPages, MajorVersion, OurVolume, Banner, EMMIDString, General
  266. PUBLIC NoEMMThere, EMMError, Init, NoMem, TooBig, BadOption, NoClicking
  267. PUBLIC Installed, DriveName, InstalledB, Installed2, UsedSpace, Bin2Dec
  268. PUBLIC Bin2DecLoop, Bin2DecDigit, WorkAreaL, WorkAreaH
  269.  
  270. ; ---------------------------------------------------------------------------
  271.  
  272. driver        segment    para public 
  273.         assume    cs:driver,ds:driver,es:driver,ss:driver
  274.  
  275.         org    0        ; drivers begin at zero
  276. firstplace    equ    this byte    ; this is the first byte
  277.  
  278. ; ---------------------------------------------------------------------------
  279. ;  Device Header
  280.  
  281. ; This area contains the header information.  It is used by DOS when loading
  282. ; the device driver, and it contains information used to describe the
  283. ; driver to the DOS environment.
  284.  
  285. NextPlace    dw    -1,-1        ; pointer to next driver
  286. Attrib        dw    00010000000000000b        ; attribute word
  287.             ;FEDCBA9876543210
  288.  
  289.                     ; device is non-ibm and block mode
  290.                     ; doesn't support IOCTL, is not
  291.                     ; a network device
  292.  
  293.         dw    offset Strategy        ; the strategy entry
  294.         dw    offset Interrupt    ; the interrupt entry
  295.         db    1,'NJ_DISK'        ;  Nifty James' Disk!
  296.  
  297. ; ---------------------------------------------------------------------------
  298.  
  299. JumpTable    label word
  300.  
  301. ; This area is a "Jump Table" that is used to dispatch the code.
  302. ; Only the functions marked with a "*" in their comment field
  303. ; are actually implemented.  (Since this is a block device, only
  304. ; some of the areas are actually used.)
  305.  
  306.     dw    offset    Init        ;  0 * initialize
  307.     dw    offset    MediaCheck    ;  1 * media check
  308.     dw    offset    BuildBPB    ;  2 * build BIOS parameter block
  309.     dw    offset    IOCTLInput    ;  3   I/O Control (Input)
  310.     dw    offset    Read        ;  4 * read from device
  311.     dw    offset    ReadNoWait    ;  5   read from device (nondest,
  312.                     ;    no wait, char only)
  313.     dw    offset    InputStatus    ;  6   input status
  314.     dw    offset    InputFlush    ;  7   flush pending input
  315.     dw    offset    Write        ;  8 * Write data
  316.     dw    offset    Write        ;  9 * Write data with Verify
  317. ;
  318. ;    dw    offset    OutputStat    ; 10   Output status
  319. ;    dw    offset    OutputFlush    ; 11   flush pending output
  320. ;    dw    offset    IOCTLOutput    ; 12   I/O Control (Output)
  321. ;    dw    offset    DeviceOpen    ; 13   Open Device
  322. ;    dw    offset    DeviceClose    ; 14   Close Device
  323. ;    dw    offset    Removeable    ; 15   Removable media check
  324. ;
  325.     ;  (The commands above 9 are all not implemented -- we don't
  326.     ;   make entries for them to optimize for space (and speed).
  327.     ;   The equate TopCommand must be set to the last used
  328.     ;   command code.)
  329.  
  330. TopCommand    equ    9        ; highest valid command
  331.  
  332. RBPoint        label    dword        ; Pointer to request buffer
  333. RBPointOff    dw    0        ; offset part
  334. RBPointSeg    dw    0        ; segment part
  335.  
  336. SaveSS        dw    0        ; save place for the SS register
  337. SaveSP        dw    0        ; save place for the SP register
  338. SaveAX        dw    0        ; save place for the accumulator
  339.  
  340. EMMHandle    dw    0        ; our handle, as assigned by the EMM
  341. EMMBase        dw    0        ; base of the EMM physical window
  342.  
  343. ; ---------------------------------------------------------------------------
  344. ;  TDATA
  345.  
  346. ;  This is a "temporary" data area that is used to hold the data used
  347. ;  by the transfer routines.
  348.  
  349. TAddr        label    dword
  350. TAddrOff    dw    0        ; the transferr (read to, write from)
  351. TAddrSeg    dw    0        ;  address
  352.  
  353. TDone        dw    0        ; count of sectors done
  354. TCount        dw    0        ; number of sectors to do
  355. TSector        dw    0        ; the sector to be transfer
  356.  
  357. ; ---------------------------------------------------------------------------
  358. ;  The local stack
  359.  
  360.         even            ; make the stack a word-aligned area
  361.         dw    64 dup (0DEADh)
  362. StackTop:
  363.  
  364. ; ---------------------------------------------------------------------------
  365. ;  Strategy Entry Point For the Device Driver
  366.  
  367. ;  This routine simply stores the pointer to the request header
  368. ;  so that request header has it.  That's all it does.  Really.
  369.  
  370. STRATPROC    proc    far
  371.  
  372. Strategy:
  373.     mov    cs:RBPointOff,bx
  374.     mov    cs:RBPointSeg,es    ; just store the pointer
  375.     ret                ; and get outta here!
  376.     ; (isn't it ironic that the shortest routine is called "Strategy"?)
  377. STRATPROC    endp
  378.  
  379. ; ---------------------------------------------------------------------------
  380. ;  Interrupt Entry Point For the Device Driver
  381.  
  382. ;  This routine executes the command contained in the passed request header.
  383. ;  DOS has called STRATEGY, and that routine stored a pointer to the request
  384. ;  header for our use.  We will construct our own stack area because the 
  385. ;  EMM uses a great deal of stack space.
  386.  
  387. INTPROC        proc    far
  388. Interrupt:    
  389. PrintChar 'D'
  390.         mov    CS:SaveSS,ss        ; save the SS register
  391.         mov    CS:SaveSP,sp        ; save the SP register
  392.         mov    CS:SaveAX,ax
  393.  
  394.         cli
  395.         mov    ax,offset StackTop    ; initialize our stack
  396.         mov    sp,ax
  397.         mov    ax,cs
  398.         mov    ss,ax
  399.         sti
  400.  
  401.     ifdef V286
  402.         pusha
  403.     else
  404.         push    bx        ; save the other regs
  405.         push    cx
  406.         push    dx
  407.         push    bp
  408.         push    si
  409.         push    di
  410.     endif
  411.  
  412.         pushf            ; and the flags
  413.         cld            ;  set the string direction up
  414.         push    es
  415.         push    ds
  416.  
  417.         mov    ds,ax        ; setup the data segment register
  418.  
  419.         mov    ax,0FFFFh    ; wipe out any memory of previous
  420.         mov    LastTime,ax    ;  page mappings
  421.  
  422.     ; Note that during calls we use DS to point to our local data
  423.     ; and ES to point to the request header.
  424.  
  425.         les    bx,RBPoint    ; get the request buffer
  426.         mov    al,rq.command    ;  get the command
  427.         cbw
  428.  
  429. ; be sure that the command is in our range
  430.  
  431.         cmp    al,TopCommand    ; fifteen is the highest for us
  432.         jg    badcommand    ;  too high! bad command
  433.  
  434. ; it's a good command - be on the lookout for an unorthodox
  435. ; initialization call
  436.  
  437.         or    al,al        ; is it function zero?
  438.         je    NoSaveM        ;  yes, don't save the map context
  439.  
  440.         push    ax        ;  no, save the AX register
  441.         mov    ah,E_Save    ; save the mapping context
  442.         mov    dx,EMMHandle    ;  under our handle
  443.         int    EMM        ; ask the manager to do it
  444.         or    ah,ah        ; if there was an error,
  445.         jnz    FreakOut    ;  freak out!
  446.         pop    ax        ; if not, get the AX back
  447.  
  448. NoSaveM:    shl    ax,1        ; (one word offset = 2 bytes)
  449.         mov    si,ax
  450.  
  451. ; fake a "short call" by setting the return address to the exit routine
  452.  
  453.         mov    ax,offset BigLog
  454.         push    ax
  455.         xor    ax,ax            ; clear our status
  456.         jmp    cs:JumpTable[si]    ; and hop to it!
  457.         jmp    short BigLog
  458.  
  459. ; ---------------------------------------------------------------------------
  460. ;   We come here if we run into an EMM error.  We'll set the "General
  461. ;   Failure" flag, and return to MS-DOS
  462.  
  463. FreakOut:    mov    rq.status,(errorflag+err_general)
  464.                         ; general failure
  465.                         ; and error settings
  466.         jmp    short BigLog
  467.  
  468. ; ---------------------------------------------------------------------------
  469. ;   Ran into an unsupported command -  set the flag in the status word.
  470.  
  471.  
  472. IOCTLInput:
  473. ReadNoWait:    ; those table entries are invalid commands
  474. InputStatus:
  475. InputFlush:
  476.         pop    ax        ; (forget about the short call)
  477. badcommand:
  478.         mov    ax,(err_unknown+errorflag)  ; an unknown command err
  479.  
  480. ; ---------------------------------------------------------------------------
  481. ;   This is the mass exit; everone splits through this point!  When we
  482. ;   arrive here, the AX reg will contain the word to be put into the
  483. ;   status word.  We'll do that:
  484.  
  485. BigLog:        
  486. PrintChar 'X'
  487.         les    bx,cs:RBPoint        ; point to the request block
  488.         push    ax
  489.         mov    al,rq.command        ; was it an init?
  490.         and    al,al            ;  yes!  don't restore
  491.         pop    ax
  492.         je    NoRestore
  493.  
  494.         push    ax
  495.         mov    ah,E_Restore        ; restore the EMS
  496.         mov    dx,EMMHandle        ;  mapping context
  497.         int    EMM
  498.         or    ah,ah            ; if there was an error
  499.         pop    ax
  500.         jnz    FreakOut
  501.  
  502. NoRestore:    or    ax,donestat        ; set the done status
  503.         mov    rq.status,ax
  504.  
  505. ;  Now, we just undo the registers.
  506.  
  507.         pop    ds        ; the seg regs
  508.         pop    es
  509.  
  510.         popf            ; the flags
  511.     ifdef V286
  512.         popa
  513.     else
  514.         pop    di
  515.         pop    si        ; and the data regs
  516.         pop    bp
  517.         pop    dx
  518.         pop    cx
  519.         pop    bx
  520.     endif
  521.  
  522. PrintChar 'd'
  523.         cli
  524.         mov    ax,CS:SaveAX
  525.         mov    sp,CS:SaveSP    ; restore the calling stack
  526.         mov    ss,CS:SaveSS
  527.         sti
  528.         ret
  529.  
  530. INTPROC        endp
  531.  
  532. ; ---------------------------------------------------------------------------
  533. ;  MEDIA CHECK
  534.  
  535. ;  This command checks to see if the media has been removed and replaced.
  536. ;  Since a RAM drive is non-removable media, this command will always
  537. ;  return a "false".
  538.  
  539. MC        proc    near
  540. PrintChar 'M'
  541. MediaCheck:
  542.         mov    rq.change,1    ; media has not been changed
  543. PrintChar 'm'
  544.         ret            ; return to leave
  545. MC        endp
  546.  
  547. ; ---------------------------------------------------------------------------
  548. ;  BUILD BIOS PARAMETER BLOCK
  549.  
  550. ;  This command simply "builds" a BPB by telling DOS where it is located.
  551.  
  552. BBPB        proc    near
  553. BuildBPB:
  554. PrintChar 'P'
  555.         mov    rq.bpboff,offset OurBPB        ; the offset
  556.         mov    rq.bpbseg,cs            ; in our CS
  557. PrintChar 'p'
  558.         ret
  559.  
  560. BPBArray    dw    offset OurBPB
  561.  
  562. OurBoot:    db    0,0,0
  563.         db    'NiftyEMS'    ;  whodat?
  564.         
  565. OurBPB:
  566. SecSize        dw    512        ; standard DOS sector size
  567. SecPerCluster    db    1        ; sectors per allocation unit
  568.         dw    1        ; number of reserved sectors
  569.         db    1        ; number of copies of the FAT
  570. RDirLen        dw    32        ; number of root directory entries
  571.  
  572. DiskSize    dw    1024        ; number of sectors on the disk
  573.         db    MediaD        ; (media descriptor)
  574. SecPerFAT    dw    1        ; number of sectors per FAT
  575.  
  576.         dw    8        ; sectors per track
  577.         dw    1        ; number of heads
  578.         dw    0        ; number of hidden sectors
  579. BootCode:
  580.  
  581. OurBootLen    equ    this byte - OurBoot
  582.  
  583. BBPB        endp
  584.  
  585.  
  586. ; ---------------------------------------------------------------------------
  587. ;  READ
  588.  
  589. ;  This command reads the specified number of sectors starting at the 
  590. ;  given sector.  It returns the number of sectors actually read.  Errors
  591. ;  are returned if the sector is out of range, or if the number of sectors
  592. ;  is past the end of the disk.  (The error checking is done in the
  593. ;  CLIPPER procedure.)  This procedure doesn't do much itself.  It's
  594. ;  body is mostly a string move instruction.  The starting address
  595. ;  and ending address are set up by the CLIPPER procedure.
  596.  
  597. RSEC        proc    near
  598. Read:
  599.  
  600.     ifdef PCL
  601.         mov    al,'N'
  602.         out    097h,al
  603.         mov    al,'J'
  604.         out    096h,al        ; display "NJ-R" on Smart-Vu
  605.         mov    al,'-'
  606.         out    095h,al
  607.         mov    al,'R'
  608.         out    094h,al
  609.     endif
  610.  
  611. PrintChar 'R'
  612.         call    CLIPPER        ; do the clipping, if need be
  613.  
  614. ReadLoop:    mov    ax,TCount    ; are we done transferring yet?
  615.         cmp    TDone,ax
  616.         je    ReadDone    ; yes!  quit the loop
  617.  
  618.         mov    ax,TSector    ; no ... do some more!
  619.         call    GetSector
  620.         jc    ReadError
  621. PrintChar 'y'
  622.  
  623.         mov    si,di        ; setup the get from address
  624.         les    di,TAddr    ; get the store to address
  625.         mov    cx,256        ; number of words to move
  626.         mov    ds,EMMBase
  627.  
  628.     rep    movsw            ; move it!
  629.  
  630.         mov    ax,cs        ; get addressing back
  631.         mov    ds,ax
  632.  
  633.         add    TAddrOff,512    ; increment transferr address
  634.         inc    TDone
  635.         inc    TSector
  636.         jmp    short ReadLoop
  637.  
  638. ReadDone:    xor    ax,ax        ; clear error flags
  639.  
  640. ReadFinish:    les    bx,[RBPoint]    ; point to request header
  641.         mov    dx,TDone    ; store actual transferred
  642.         mov    rq.Count,dx
  643.         jmp    MakeClick    ; finish clicking
  644.  
  645. ReadError:    mov    ax,(err_badread+errorflag)  ; there was an error!
  646.         jmp    short ReadFinish
  647.  
  648. RSEC        endp
  649.  
  650.  
  651. ; ---------------------------------------------------------------------------
  652. ;  WRITE
  653.  
  654. ;  This command writes the specified number of sectors starting at the
  655. ;  given sector.  It returns the number of sectors actually written.
  656. ;  Errors are returned if the sector is out of range, or if the number
  657. ;  of sectors is past the end of the disk.  (The error checking is done
  658. ;  in the CLIPPER procedure.)  The procedure does very little itself;
  659. ;  it's body consists mostly of a string move instruction.  The
  660. ;  source, destination, and other counts are set up by the CLIPPER
  661. ;  procedure.
  662.  
  663. WSEC        proc    near
  664. Write:
  665.     ifdef PCL
  666.         mov    al,'N'
  667.         out    097h,al
  668.         mov    al,'J'
  669.         out    096h,al        ; display "NJ-W" on Smart-Vu
  670.         mov    al,'-'
  671.         out    095h,al
  672.         mov    al,'W'
  673.         out    094h,al
  674.     endif
  675.  
  676. PrintChar 'W'
  677.         call    CLIPPER        ; do the clipping, if need be
  678.  
  679. WriteLoop:    mov    ax,TCount    ; are we done transferring yet?
  680.         cmp    TDone,ax
  681.         je    WriteDone    ; yep, we are, quit the loop
  682.  
  683.         mov    ax,TSector    ; no ... do some more!
  684.         call    GetSector
  685.         jc    WriteError
  686. PrintChar 'k'
  687.         mov    es,EMMBase
  688.         lds    si,cs:TAddr    ; get the store to address
  689.         mov    cx,256        ; number of words to move
  690.  
  691.     rep    movsw            ; move it!
  692.  
  693.         mov    ax,cs        ; reset addressing back
  694.         mov    ds,ax
  695.  
  696.         add    TAddrOff,512    ; increment transferr address
  697.         inc    TDone        ; count of sectors
  698.         inc    TSector        ; and current sector number
  699.         jmp    short WriteLoop
  700.  
  701. WriteDone:    xor    ax,ax        ; clear error flags
  702.  
  703. WriteFinish:    les    bx,[RBPoint]    ; point to request header
  704.         mov    dx,TDone    ; store actual transferred
  705.         mov    rq.Count,dx
  706.         jmp    MakeClick    ; finish clicking
  707.  
  708. WriteError:    mov    ax,(err_badwrite+errorflag)  ; there was an error!
  709.         jmp    short WriteFinish
  710.  
  711.  
  712. WSEC        endp
  713.  
  714. ; ---------------------------------------------------------------------------
  715. ;  Clipper
  716.  
  717. ;  This local procedure checks the parameters passed to the READ and
  718. ;  WRITE commands to be sure that they are valid.  If they are indeed
  719. ;  valid, it will call the speaker click procedure to take care of
  720. ;  the "audible" options.  It also saves the context of the EMM, and 
  721. ;  sets up the EMM to work with our process.
  722.  
  723. CLIPPER        proc    near
  724.  
  725.         mov    cx,rq.strtsec    ; get the starting sector number
  726.         mov    TSector,cx
  727.         cmp    cx,DiskSize    ; is it larger than the drive?
  728.         jg    RangeError    ;  yes!  there's an error
  729.  
  730.         mov    ax,rq.count
  731.         mov    TCount,ax        ; save it for later
  732.         add    cx,ax        ; add in the number of sec to read
  733.         cmp    cx,DiskSize    ; is it larger than life?
  734.         jle    InRange        ;  no... it's okay
  735.  
  736. RangeError:    pop    ax        ; forget our our return address
  737.         mov    ax,err_badsector; that's a bad sector!
  738.         or    ax,errorflag    ;  (and that's an error in my book)
  739.         mov    rq.count,0    ; no sectors were read, you know
  740.         ret            ;  return back to the dispatcher
  741.  
  742. InRange:    mov    ax,rq.tbaoff        ; get the transfer base addr
  743.         mov    TAddrOff,ax
  744.         mov    ax,rq.tbaseg
  745.         mov    TAddrSeg,ax
  746.         xor    ax,ax        ; zero transferred count
  747.         mov    TDone,ax
  748.  
  749.         ; just flow through to MakeClick
  750.  
  751. CLIPPER        endp
  752.  
  753. ; ---------------------------------------------------------------------------
  754. ;  This is a local procedure that clicks the speaker transparently.  It
  755. ;  is executed at the end of the CLIPPER procedure, which is excuted
  756. ;  at the very beginning of the "READ" and "WRITE" functions.  It is
  757. ;  also JMP'd to at the end of the READ and WRITE routines, and the RET
  758. ;  at the end of this procedure will return to the caller of the READ
  759. ;  and WRITE functions.  (Saves 2 bytes and a bunch of clocks, hey.)
  760.  
  761. SPEAKERCLICK    proc    near
  762.  
  763. MakeClick:    pushf
  764.         cmp    SpeakerFlag,0        ; should we?
  765.         je    NoClick            ;  no, forget it happened
  766.  
  767.         push    ax            ; yes, save the accumulator
  768.         in    al,Speak
  769.         and    al,SpeakMask        ; mask out the bit we don't need
  770.         xor    al,SpeakToggle        ; toggle the control bit
  771.         out    Speak,al        ; and re-output it
  772.         pop    ax            ; retrieve the accumulator
  773.  
  774. NoClick:    popf
  775.         ret            ; return to the caller
  776.  
  777. SpeakerFlag    db    1        ; one if we should be ticking
  778.                     ;    (the default is ticking)
  779.  
  780. SPEAKERCLICK    endp
  781.  
  782. ; ---------------------------------------------------------------------------
  783. ;  GetSector
  784.  
  785. ;  This routine calls the EMM to map the page with the requested sector
  786. ;  into the physical window.  On entry, AX contains the requested sector.
  787. ;  On exit, the EMS is setup so that the requested sector is in the window.
  788. ;  [EMMBASE]:DI will point to it.  This routine is rather funky; it does
  789. ;  the mapping using the slippery shift functions, instead of using DIV
  790. ;  or a lookup table.  *SUPER FAST*!
  791.  
  792. GS        proc    near
  793.  
  794. GetSector:
  795. PrintChar 'G'
  796.         cmp    ax,DiskSize    ; check the range!
  797.         jg    CantGet
  798.  
  799.         push    ax        ; save a copy of the number
  800.     ifdef    V286
  801.         shr    ax,5
  802.     else
  803.         mov    cl,5
  804.         shr    ax,cl        ; divide the sector by 32
  805.                     ;   so that AX=EMM Page
  806.     endif
  807.  
  808.         cmp    ax,LastTime    ; is it the same thing 
  809.         je    Optimized    ;  we got last time?
  810.  
  811.         mov    LastTime,ax    ; remember it for later
  812.  
  813.         mov    bx,ax        ; nah, we'll have to get this one
  814.         mov    ah,E_MapPage
  815.         mov    al,0        ; map it into zero
  816.         mov    dx,EMMHandle
  817.         int    EMM
  818.         or    ah,ah        ; was there an error?
  819.         jne    CantGet
  820.  
  821. Optimized:    pop    ax        ; retrieve the remainder
  822.         and    ax,01Fh        ; mask out high bits of offset
  823.     ifdef V286
  824.         shl    ax,9
  825.     else
  826.         mov    cl,9
  827.         shl    ax,cl        ; find the offset of the sector
  828.     endif
  829.         mov    di,ax
  830.         clc
  831. PrintChar 'g'
  832.         jmp    short MakeClick    ; tick-tock on the way out
  833.  
  834. CantGet:    pop    ax    ; forget the remainder, since there was err
  835.         stc        ; set the error flag, if error
  836.         ret
  837.  
  838. ; An ingenious optimization, if I must say so myself.  This variable holds
  839. ; the last EMS logical page that was fetched by this routine.  This way,
  840. ; the program never gets the same page twice in a row.  It's reset to
  841. ; 0FFFFh by the Interrupt routine so that we won't forget to get a page
  842. ; when one hasn't been attained.  (0FFFFh is a unique code that will never
  843. ; correspond to an actual page.)  Since DOS often does more than one
  844. ; sequential read or write in a single call to the driver, this small
  845. ; feature can save quite a bit of time.
  846.  
  847. LastTime    dw    0FFFFh
  848.  
  849. GS        endp
  850.  
  851. ; ---------------------------------------------------------------------------
  852. ;  This label marks the last byte of the device driver that actually
  853. ;  remains resident.  This driver takes less than 800 bytes, guaranteed.
  854.  
  855. LastResident:
  856. ; ---------------------------------------------------------------------------
  857. ;  INITIALIZE
  858.  
  859. ;  This command sets up the internal data used by NJRAMD.  The procedure
  860. ;  sets the EMM to get the number of pages that the user requests.  (The
  861. ;  information following the specification in the CONFIG.SYS file is
  862. ;  parsed to find the user parameters.  See the NJFRAMD.DOC file to find
  863. ;  the format of the CONFIG information.)  The procedure requests memory
  864. ;  from the EMM
  865.  
  866. Init:
  867. PrintChar 'I'
  868.         mov    dx,offset Banner
  869.         mov    ah,PrintString        ; show our copyright!
  870.         int    21h
  871.  
  872.         mov    ah,GetDOSVersion    ; get the DOS version
  873.         int    21h
  874.         mov    MajorVersion,al
  875.  
  876.         xor    ax,ax            ; point to the 0000 segment
  877.         mov    es,ax
  878.         mov    bx,(EMM*4)+2        ; find the EMM interrupt
  879.         mov    ax,es:[bx]
  880.         mov    es,ax            ; point to the EMM device
  881.         mov    di,10            ; header
  882.  
  883.         mov    si,offset EMMIDString    ; point to the EMM identifier
  884.         mov    cx,8
  885.     repz    cmpsb                ; compare a bunch of bytes
  886.         jz    EMMPresent
  887.  
  888.         mov    dx,offset NoEMMThere    ; point to our error
  889.         jmp    InitFail        ;  the EMM isn't there!!
  890.  
  891. EMMPresent:    ; the Extended Memory Manager is present.  It's okay!
  892.         ; get the EMM Page base, and save it for future reference.
  893.  
  894.         mov    ah,E_PageBase        ; get the page base
  895.         int    EMM
  896.         or    ah,ah
  897.         je    EMMPresent2        ; general failure?
  898. GenFailHook:    jmp    GenFail            ;  (RELATIVE JMP OUT OF RANGE
  899.  
  900. EMMPresent2:    mov    EMMBase,bx        ; save it for later
  901.  
  902.         mov    ah,E_Counts        ; get count of available
  903.         int    EMM            ; memory
  904.         or    ah,ah
  905.         jne    GenFailHook        ; general failure?
  906.                         ;  (RELATIVE JMP OUT OF RANGE)
  907.  
  908.         cmp    bx,0        ; is there any left for me?
  909.         jne    MemForMe
  910.  
  911.         mov    dx,offset NoMem    ; print error
  912.         jmp    InitFail
  913.  
  914. MemForMe:    mov    HowMuch,bx        ; remember how much is left
  915.  
  916.     ; We will now attempt to parse the line of the CONFIG.SYS
  917.     ; file to see if any of our options are on it.
  918.  
  919.         les    bx,[RBPoint]        ; get pointer to header
  920.         les    si,es:[bx+18]        ; get pointer to commands
  921.  
  922. EatingWhite:    mov    al,es:[si]        ; get the next byte
  923.         inc    si
  924.         cmp    al,cr            ; is it a carriage return?
  925.         je    EndOfLine
  926.         cmp    al,'-'            ; is it an option marker?
  927.         je    GotOption        ; yeah! go process it!
  928.         cmp    al,'/'
  929.         jne    EatingWhite        ; no... go back for more
  930.  
  931.     ; We are now pointing at the text of an option.  We will
  932.     ; get the option into the al to see exactly what it is, and we
  933.     ; will then act accordningly.
  934.  
  935. GotOption:    mov    al,es:[si]        ; get the option
  936.         inc    si            ;  and increment the pointer
  937.         cmp    al,'a'            ; bump it to upper case?
  938.         jl    NoBump            ;  no need to
  939.         cmp    al,'z'
  940.         jg    NoBump            ;  no need to
  941.  
  942.         sub    al,('a' - 'A')        ; make it lower case
  943.         
  944. NoBump:        cmp    al,'S'            ; is it a silence option?
  945.         jne    NotSilence        ;   no...
  946.         mov    SpeakerFlag,0        ; yes, it is.  Reset the option!
  947.         jmp    EatingWhite        ; and eat up until end of
  948.                         ;  this option
  949.  
  950. NotSilence:    cmp    al,'P'            ; is it the pages option?
  951.         jne    NotPages
  952.  
  953.     ; We will handle the pages option by reading the command line until
  954.     ; a non-numeric character.  The resulting number will be the number
  955.     ; of pages that the user requested.
  956.  
  957.         xor    dx,dx        ; zero the result
  958.  
  959. PagesLoop:    mov    al,es:[si]    ; get the character
  960.         inc    si
  961.         cmp    al,'0'        ; is it a number?
  962.         jl    LastDigit    ;  nope!
  963.         cmp    al,'9'        ; is it a number?
  964.         jg    LastDigit    ;  note!
  965.  
  966.         push    ax        ; save the digit temporarily
  967.         mov    ax,10
  968.         mul    dx        ; multiply it out
  969.         pop    dx        ; pop the digit into dx
  970.  
  971.         and    dx,0Fh        ; make a decimal digit of it
  972.         add    dx,ax        ; add it into the sum
  973.         jmp    short PagesLoop
  974.  
  975. LastDigit:    mov    RqdPages,dx    ; save requested number of pages
  976.         and    dx,dx        ; is the requested page number zero?
  977.         je    BadPages    ;  yeah!  can't have that
  978.         cmp    al,cr        ; was that last char a CR?
  979.         je    EndOfLine    ;  yes! end of the parse
  980.         jne    EatingWhite    ;  no, go back for more parsing
  981.  
  982. BadPages:    mov    dx,offset TooSmall
  983. BadPages2:    jmp    InitFail
  984.  
  985. NotPages:    cmp    al,'A'            ; is it use all memory?
  986.         jne    NotUseAll
  987.  
  988.         mov    ax,HowMuch
  989.         mov    RqdPages,ax        ; request them all
  990.         jmp    EatingWhite
  991.  
  992. NotUseAll:
  993. Unrecognized:    mov    dx,offset BadOption    ; don't install
  994.         jmp    short BadPages2
  995.  
  996.  
  997.  
  998. EndOfLine:    ; The parsing is done!  We will now check to see if the
  999.         ; requested size is bigger than the available memory.
  1000.  
  1001.         mov    ax,RqdPages        ; is the reqested amount
  1002.         cmp    HowMuch,ax        ;  greater than available?
  1003.         jge    GoodSize        ; no, size is good
  1004.  
  1005.         mov    dx,offset TooBig    ; yes, that's an error
  1006.         jmp    InitFail
  1007.  
  1008. GoodSize:    ; Now, we'll try to allocate that many pages.  If the user
  1009.         ; didn't specify a number of pages, the default is 32 pages,
  1010.         ; which is 512k of storage.
  1011.  
  1012.         mov    bx,ax
  1013.         mov    ah,E_Open    ; open a new handle of (BX) pages
  1014.         int    EMM
  1015.         or    ah,ah
  1016.         je    GotPages    ; (RELATIVE JMP OUT OF RANGE)
  1017.         jmp    GenFail
  1018.  
  1019. GotPages:    mov    EMMHandle,dx        ; save the handle for later
  1020.  
  1021.     ; We will now setup the information in the BPB to reflect the
  1022.     ; status of the RAM drive.  First, we'll store the DiskSize.
  1023.  
  1024.         mov    ax,RqdPages        ; get number of pages
  1025.     ifdef    V286
  1026.         shl    ax,5
  1027.     else
  1028.         mov    cl,5            ; thirty-two 512-byte sectors
  1029.         shl    ax,cl            ;  in a 16384-byte page
  1030.     endif
  1031.         mov    DiskSize,ax        ; store it in BPB
  1032.  
  1033.     ; Now, we'll figure out how many entries there will be in the
  1034.     ; root directory.  We will allow 1 root directory entry for
  1035.     ; each 2k of storage that the disk has.  We won't allow moer
  1036.     ; than 512 root dir entries, though.
  1037.  
  1038.     ifdef    V286
  1039.         shr    ax,2
  1040.     else
  1041.         shr    ax,1            ; figure out length of
  1042.         shr    ax,1            ; root directory
  1043.     endif
  1044.         cmp    ax,512            ;  1 entry per 2k of storage
  1045.         jl    BigBust            ;  up to 512
  1046.  
  1047.         mov    ax,512
  1048.  
  1049. BigBust:    add    ax,31            ; make sure it's a multiple
  1050.         and    ax,not 31        ; of 32  (round it)
  1051.         mov    RDirLen,ax
  1052.  
  1053.     ; Since we use a 12-bit FAT, we must have 4087 clusters or less.
  1054.     ; We will start with a 1024-byte cluster, and double the cluster
  1055.     ; size until we have enough FAT space.  The maximum amount of
  1056.     ; memory on a single EMS card is 2 megabytes.  A user must
  1057.     ; configure about 3.75 megabytes of memory as a RAM drive to
  1058.     ; cause the program to use 2048-byte clusters... otherwise, the
  1059.     ; drive will have 1024-byte clusters.
  1060.  
  1061.         mov    cx,2            ; Two clusters per sector
  1062.                         ;  for starters.
  1063.  
  1064. ReTry:        mov    ax,DiskSize        ; get the disk size
  1065.         xor    dx,dx
  1066.         div    cx            ; AX = (DiskSize/SPC)
  1067.         cmp    ax,4087            ; is it less than 4087?
  1068.         jl    GoodCombo        ;  yeah!
  1069.         shl    cx,1            ; no. double the SPC and
  1070.         jnc    ReTry            ; try it again
  1071.  
  1072. GoodCombo:    mov    SecPerCluster,cl    ; save SPC number
  1073.  
  1074.     ; AX still is set to the number of clusters on the disk.  Very
  1075.     ; useful number, you know.  We will find now the amount of FAT
  1076.     ; space that is needed.
  1077.  
  1078.         mov    bx,ax        ; ax = clustsers
  1079.         add    ax,ax        ; ax = 2*(clusters)
  1080.         add    ax,bx        ; ax = 3*(clusetrs)
  1081.         shr    ax,1        ; ax = 1.5*(clusters)
  1082.  
  1083.         xor    dx,dx        ;      (FAT Length)
  1084.         mov    cx,512        ; AX =    ----------------
  1085.         div    cx        ;       (BytesPerSector)
  1086.  
  1087.         or    dx,dx        ; is there a remainder?
  1088.         je    NoKludge
  1089.         inc    ax        ;  yes, add another sector
  1090.  
  1091. NoKludge:    mov    SecPerFAT,ax    ; store it in the BPB
  1092.  
  1093.     ; The BPB is now set up properly.  We will now "format" the
  1094.     ; RAM disk.  First, we will have to set all the RAM area to
  1095.     ; zero.  (Even on extremely large "drives", this doesn't take
  1096.     ; very long.  Especially if you have an IBM PS/2 System 80 --
  1097.     ; and then, all the chicks will dig ya!)
  1098.  
  1099.         mov    cx,RqdPages    ; get number of pages in disk
  1100.  
  1101. WipeOut:    mov    bx,cx        ; ask for this page
  1102.         dec    bx
  1103.         mov    dx,EMMHandle    ; into physical page zero
  1104.         mov    ah,E_MapPage
  1105.         mov    al,0
  1106.         int    EMM
  1107.  
  1108.         or    ah,ah
  1109.         je    WipeOut2    ; if there was an error, get out
  1110.         jmp    GenFail        ;    (RELATIVE JMP OUT OF RANGE)
  1111.  
  1112. WipeOut2:    mov    es,EMMBase    ; get addressing to it
  1113.         xor    ax,ax        ; store a zero
  1114.         mov    di,ax        ; zero the destination
  1115.         push    cx
  1116.         mov    cx,8192        ; *words* in a page
  1117.  
  1118.         rep    stosw
  1119.  
  1120.         pop    cx
  1121.         loop    WipeOut        ; if more, go back
  1122.  
  1123.     ; Now that everything is zeroed, we will copy the pseudo-boot
  1124.     ; sector that we have.  DOS uses some of this information while
  1125.     ; reading and writing the disk, so we set it up there.
  1126.  
  1127.         xor    ax,ax        ; get the 0 sector
  1128.         call    GetSector
  1129.         mov    es,EMMBase
  1130.         mov    si,offset OurBoot
  1131.         mov    cx,OurBootLen
  1132.     rep    movsb            ; move it in there
  1133.  
  1134.     ; The boot sector has been written in.  We will now set up
  1135.     ; the FAT.  This task is rather simplified, since we only
  1136.     ; have one copy of the FAT.
  1137.  
  1138.         mov    ax,1
  1139.         call    GetSector    ; get sector 1
  1140.         mov    byte ptr es:[di],MediaD
  1141.         mov    word ptr es:[di+1],0FFFFh
  1142.  
  1143.     ; Now, we will figure out where the first directory sector is.
  1144.     ; *WARNING* - This code assumes that there is only one copy of
  1145.     ; the FAT, and that there is one reserved sector.  If ya change
  1146.     ; the drive to have 2 copies of the FAT, or modify it to have
  1147.     ; reserved sectors (for whatever reason you'd wanna do that),
  1148.     ; you'll have to change this code fragment!
  1149.  
  1150.         mov    ax,SecPerFAT
  1151.         inc    ax        ; AX = first dir sector
  1152.         call    GetSector
  1153.         mov    si,offset OurVolume
  1154.         mov    cx,OurVolumeLen        ; move words
  1155.     rep    movsw
  1156.  
  1157.     ; Phew!  Now the whole thing is done!  We will show the user
  1158.     ; what has been done.  First, we will figure out what device
  1159.     ; tag that we have.  We will tell the user about it.  DOS versions
  1160.     ; earlier than 3.00 don't let us know what our device tag is,
  1161.     ; so we can't tell the user.
  1162.  
  1163.         les    bx,[RBPoint]    ; point to the header, again
  1164.         mov    al,rq.taglet    ; get the tag letter
  1165.         add    al,'A'        ; change it to a capital drive letter.
  1166.         mov    DriveName,al
  1167.  
  1168.         mov    bx,offset LastResident    ; calculate used size
  1169.         xor    ax,ax
  1170.         mov    si,offset UsedSpace
  1171.         call    Bin2Dec            ; store it in the messgae
  1172.  
  1173.         mov    ah,E_Counts        ; find amount of space left
  1174.         int    EMM            ; in the EMS memory
  1175.         or    ah,ah
  1176.         je    FindFree
  1177.         jmp    GenFail
  1178.  
  1179. FindFree:    xor    ax,ax        ; zero high side (the EMM call put
  1180.                     ;  the # of free pages in BX.)
  1181.         mov    cx,14        ; multiply the # of pages by 16k
  1182. CalcEMMFree:    rcl    bx,1        ; shift it low side
  1183.         rcl    ax,1        ;  and carry though to high side
  1184.         loop    CalcEMMFree
  1185.  
  1186.         mov    si,offset Installed2
  1187.         call    Bin2Dec        ; put it into the message!
  1188.  
  1189.         mov    ax,DiskSize    ; get sectors of disk space
  1190.         sub    ax,SecPerFAT    ; subtract space used by the FAT
  1191.         mov    bx,ax        ; put that subtotal in bx
  1192.  
  1193.         mov    ax,RDirLen    ; get the entries in the root dir
  1194.     ifdef V286
  1195.         shr    ax,4
  1196.     else
  1197.         mov    cl,4
  1198.         shr    ax,cl        ; divide by # of entries per sec
  1199.     endif
  1200.         sub    bx,ax        ; subtract some more
  1201.         dec    bx        ; and adjust down
  1202.  
  1203.         mov    cx,9        ; multiply the answer by 512
  1204.         xor    ax,ax        ; zero the high side
  1205. CalcDiskFree:    rcl    bx,1        ; shift low side up
  1206.         rcl    ax,1        ;  shift high side over, with carry
  1207.         loop    CalcDiskFree
  1208.  
  1209.         mov    si,offset Installed
  1210.         call    Bin2Dec        ; store it in the message
  1211.  
  1212.         mov    al,SpeakerFlag        ; is there clicking?
  1213.         or    al,al
  1214.         jne    ClickOkay
  1215.         mov    ah,PrintString
  1216.         mov    dx,offset NoClicking    ; tell the user that it's
  1217.         int    21h            ; been diasbled.
  1218.  
  1219. ClickOkay:    mov    al,MajorVersion
  1220.         cmp    al,3        ; is it version three?
  1221.         je    MsgOkay
  1222.  
  1223.         mov    al,eos        ; its version two
  1224.         mov    DriveName,al
  1225.     
  1226. MsgOkay:    mov    dx,offset Installed    ;print part one of 
  1227.         mov    ah,PrintString        ;  installed! message
  1228.         int    21h
  1229.  
  1230.         mov    dx,offset InstalledB    ;print part two
  1231.         int    21h
  1232.  
  1233.         les    bx,[RBPoint]        ; get that pesky pointer
  1234.  
  1235.         mov    ax,offset LastResident     ; show DOS where we end
  1236.         mov    rq.ndadro,ax        ;   offset
  1237.         mov    ax,cs            ; show DOS were we end
  1238.         mov    rq.ndadrs,ax        ;   segment
  1239.         mov    rq.bpbseg,ax        ; show DOS the BPB array
  1240.  
  1241.         mov    rq.units,1        ; we installed one unit
  1242.         mov    ax,offset BPBArray    ; BPB array offset
  1243.         mov    rq.bpboff,ax
  1244.  
  1245.         xor    ax,ax        ; no return value
  1246.         ret
  1247.  
  1248. ; ---------------------------------------------------------------------------
  1249. ;  Init failure
  1250.  
  1251. ;  We will come here if there is a failure during the initialization
  1252. ;  of the driver.  We print a message letting the user know why we can't
  1253. ;  install, and we then zero ourselves out so that DOS doesn't waste any
  1254. ;  memory on us.
  1255.  
  1256. InitFail:    push    dx        ; save the specific error
  1257.         mov    dx,offset General
  1258.         mov    ah,PrintString
  1259.         int    21h
  1260.  
  1261.         pop    dx        ; now print specific error
  1262.         mov    ah,PrintString
  1263.         int    21h
  1264.  
  1265.         les    bx,[RBPoint]    ; point to the request header
  1266.         mov    ax,cs
  1267.  
  1268.         mov    rq.ndadrs,ax    ; ending address is zero
  1269.  
  1270.         xor    ax,ax        ;  because no memory is taken
  1271.         mov    rq.ndadro,ax    ;  since we failed
  1272.         mov    rq.units,al    ; no units, either
  1273. PrintChar 'i'
  1274.         ret
  1275.  
  1276. ; ---------------------------------------------------------------------------
  1277. ;  General Failure
  1278.  
  1279. ;  There was an EMM Failure during the installation.  If such is the case,
  1280. ;  we will terminate with an error message, and then go to the regular
  1281. ;  fail routine.
  1282.  
  1283. GenFail:    mov    dx,offset EMMError
  1284.         jmp    short InitFail
  1285.  
  1286.  
  1287. ; ---------------------------------------------------------------------------
  1288. ;  Transient Data Area
  1289.  
  1290. ;  The TDA contains the variables used by the Initialization segment of
  1291. ;  the device driver.  It doesn't stay resident.
  1292.  
  1293. HowMuch        dw    ?        ; amount of free EMS, in pages
  1294. RqdPages    dw    32        ; amount of pages requested
  1295.                     ;    (512k is the default)
  1296. MajorVersion    db    3        ; the DOS major version number
  1297.  
  1298. OurVolume    db    'Niftys_Disk'    ; 11-byte volume name
  1299.         db    000001000b    ;  volume label attribute
  1300.         db    10 dup (0)    ; reserved space
  1301.         dw    953
  1302.             ;FEDCBA9876543210b
  1303.             ;YYYYYYYMMMMDDDDD
  1304.         dw    00000111101011111b    ; DATE = Oct 31, 1987
  1305.         db    6 dup (0)    ; more reserved space
  1306.  
  1307. OurVolumeLen    equ    16
  1308.  
  1309. ; ---------------------------------------------------------------------------
  1310. ;  Messages
  1311.  
  1312. ;  These are messages that are used by the initialization section of the
  1313. ;  driver.
  1314.  
  1315. Banner        db    cr,lf,"Nifty James Famous E/EMS RAMdisk Drive",cr,lf
  1316.         db    'Copyright 1987 by Mike Blaszczak',cr,lf
  1317.         db    'Version 1.40ASP of 23 June, 1991',cr,lf
  1318.     ifdef V286
  1319.     ifdef PCL
  1320.         db    "(PC's Limited Version)",cr,lf
  1321.     else
  1322.         db    '(Enhanced Processor Version)',cr,lf
  1323.     endif
  1324.     endif
  1325.         db    lf,eos
  1326.  
  1327. EMMIDString    db    'EMMXXXX0'
  1328.  
  1329. General        db    'Device not installed.',cr,lf,eos
  1330.  
  1331. NoEMMThere    db    'The EMM is not installed.',cr,lf,lf,eos
  1332.  
  1333. EMMError    db    'EMM failure during installation.',cr,lf,lf,eos
  1334.  
  1335. NoMem        db    'No free EMM Memory.',cr,lf,lf,eos
  1336.  
  1337. TooBig        db    'Requested size too big to fit.',cr,lf,lf,eos
  1338.  
  1339. TooSmall    db    "Can't have zero disk size.",cr,lf,lf,eos
  1340.  
  1341. BadOption    db    'Unrecognized option encountered.',cr,lf,lf,eos
  1342.  
  1343. NoClicking    db    'Clicking suppressed.',cr,lf,eos
  1344.  
  1345. ; "Installed" marks the beginning of the information that is printed
  1346. ; if the device is successfully installed.  The beginning of each
  1347. ; line has eight spaces, which are filled with the information by the
  1348. ; BIN2DEC procedure.  There is then one more space, so that the end
  1349. ; of the number doesn't bump the first word... thus, a total of nine
  1350. ; spaces begin the Installed, Installed2, and UsedSpace labels.
  1351.  
  1352. Installed    db    '         bytes available on RAM drive '
  1353. DriveName    db    '_:.',eos
  1354.  
  1355. InstalledB    db    cr,lf
  1356. Installed2    db    '         bytes left in EMS storage.',cr,lf
  1357. UsedSpace    db    '         bytes of standard DOS memory were'
  1358.         db    ' taken.',cr,lf,lf
  1359.         db    eos
  1360.  
  1361. ; ---------------------------------------------------------------------------
  1362. ;  Init Subroutines
  1363.  
  1364. ;  The following area contains subroutines used by the INIT procedure of
  1365. ;  the device driver.  They aren't kept in memory after the device has been
  1366. ;  installed.
  1367.  
  1368. ; ---------------------------------------------------------------------------
  1369. ;  BIN2DEC
  1370.  
  1371. ;  This routine converts a binary number, in AX:BX, to decimal notation.
  1372. ;  It will convert up to 8 digits, and will supress leading zeros.  The
  1373. ;  routine should be called with DS:SI set to point to the area to store
  1374. ;  the converted number.
  1375.  
  1376. Bin2Dec        proc    near
  1377.  
  1378.         push    es        ; save the registers
  1379.         push    ds
  1380.         push    di
  1381.         push    si
  1382.  
  1383.         mov    WorkAreaL,bx
  1384.         mov    WorkAreaH,ax    ; put the number on our scratchpad
  1385.  
  1386.         mov    ax,ds        ; point to the answer with ES:DI
  1387.         mov    es,ax
  1388.         mov    di,si
  1389.         add    di,7
  1390.  
  1391.         mov    si,offset WorkAreaL    ; point at scratchpad
  1392.  
  1393. Bin2DecLoop:    push    si
  1394.  
  1395.         xor    bx,bx        ; done flag
  1396.         mov    cx,2        ; 2 words in our number
  1397.         mov    dx,bx        ; clear remainder
  1398.         add    si,2        ; point to the high end
  1399.  
  1400. Bin2DecDigit:    push    cx        ; save word count
  1401.         mov    ax,[si]        ; get the digit
  1402.         mov    cx,10
  1403.         div    cx        ; convert it
  1404.         mov    [si],ax        ; store it back
  1405.         or    bx,ax        ; set the done flag appropriately
  1406.         sub    si,2        ; point to next lower
  1407.         pop    cx
  1408.         loop    Bin2DecDigit
  1409.  
  1410.         or    dl,'0'        ; make it into a decimal digit
  1411.         mov    [di],dl        ; and store it
  1412.         dec    di        ; adjust pointer
  1413.  
  1414.         pop    si        ; get the pointer back
  1415.         and    bx,bx        ; is the result zero?
  1416.         jne    Bin2DecLoop    ; nope!  Do more!
  1417.  
  1418.         pop    si        ; retrieve the used registers
  1419.         pop    di
  1420.         pop    ds
  1421.         pop    es
  1422.         ret
  1423.  
  1424. WorkAreaL    dw    0        ; low end of the work area
  1425. WorkAreaH    dw    0        ; high side of the work area
  1426.  
  1427. Bin2Dec        endp
  1428.  
  1429. driver        ends
  1430.         end
  1431.  
  1432. ; That's a wrap.
  1433. ; Special thanks to Bob Brody and Dr File Finder~~~~~~~~~~~~.
  1434.  
  1435. 
  1436.